#!/bin/bash
# Apache线程池耗尽(无法处理新请求)故障修复脚本
# 适用于Linux系统中Apache服务线程池资源耗尽的情况
FIX_CODE_ID="apache-001"


# 配置参数
LOG_FILE="/var/log/apache_thread_pool_fix.log"
BACKUP_DIR="/etc/apache2/conf_backups"
INCREASE_PERCENT=20
MAX_ADJUSTMENTS=3
WHITELIST_USERS=("www-data" "apache")
DEFAULT_SERVER_NAME="localhost"

# Apache配置文件路径
APACHE_CONFIG_PATHS=(
    "/etc/httpd/conf/httpd.conf"
    "/etc/apache2/apache2.conf"
)

# 初始化日志
log() {
    local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
    echo "[$timestamp] $1" >> "$LOG_FILE"
    echo "[$timestamp] $1"
}

# 检查Apache是否运行
check_apache_running() {
    if systemctl is-active --quiet apache2 || systemctl is-active --quiet httpd; then
        return 0
    else
        log "Apache服务未运行"
        return 1
    fi
}

# 查找有效的Apache配置文件
find_apache_config() {
    for config in "${APACHE_CONFIG_PATHS[@]}"; do
        if [ -f "$config" ]; then
            echo "$config"
            return 0
        fi
    done
    log "未找到Apache配置文件"
    return 1
}

# 检测线程池是否耗尽（核心修改部分）
detect_thread_pool_exhaustion() {
    # 1. 多途径获取Apache状态信息
    local apache_status=""
    local status_methods=(
        "apache2ctl fullstatus 2>/dev/null"  # 完整状态（需要mod_status启用）
        "apache2ctl status 2>/dev/null"      # 简化状态
        "httpd -M 2>/dev/null; httpd -V 2>/dev/null"  # 备选方案（适用于无status模块）
        "ps aux | grep -E 'apache2|httpd' | grep -v grep"  # 进程信息作为最后备选
    )

    # 尝试所有状态获取方法，直到成功
    for method in "${status_methods[@]}"; do
        apache_status=$(eval "$method")
        if [ -n "$apache_status" ]; then
            break
        fi
    done

    if [ -z "$apache_status" ]; then
        log "无法获取Apache状态信息，可能未安装状态模块或权限不足"
        return 1
    fi

    # 2. 增强版数值提取（适配多种格式）
    local active_threads
    local idle_threads
    local waiting_requests

    # 提取活跃线程（支持多种关键词和格式）
    active_threads=$(echo "$apache_status" | grep -iE "Active (threads|workers|connections)" | 
                     sed -E 's/.*[[:space:]]+([0-9]+)[[:space:]]*$/\1/; s/[^0-9]//g')
    # 提取空闲线程
    idle_threads=$(echo "$apache_status" | grep -iE "Idle (threads|workers)" | 
                   sed -E 's/.*[[:space:]]+([0-9]+)[[:space:]]*$/\1/; s/[^0-9]//g')
    # 提取等待请求
    waiting_requests=$(echo "$apache_status" | grep -iE "Waiting (for|on) (connection|request|queue)" | 
                       sed -E 's/.*[[:space:]]+([0-9]+)[[:space:]]*$/\1/; s/[^0-9]//g')

    # 3. 兜底默认值（如果提取失败）
    active_threads=${active_threads:-0}
    idle_threads=${idle_threads:-0}
    waiting_requests=${waiting_requests:-0}

    log "当前Apache线程状态: 活跃线程=$active_threads, 空闲线程=$idle_threads, 等待请求=$waiting_requests"

    # 4. 验证数值有效性并判断状态
    if ! [[ "$idle_threads" =~ ^[0-9]+$ ]] || ! [[ "$waiting_requests" =~ ^[0-9]+$ ]]; then
        log "线程状态数据无效，使用备选判断逻辑"
        # 备选逻辑：通过进程数判断（如果有大量Apache进程，可能表示线程池紧张）
        local apache_processes=$(ps aux | grep -E 'apache2|httpd' | grep -v grep | wc -l)
        log "当前Apache进程数: $apache_processes"
        if [ "$apache_processes" -gt 100 ]; then  # 阈值可根据实际情况调整
            log "检测到大量Apache进程，可能线程池紧张"
            return 0
        else
            return 1
        fi
    fi

    # 正常判断逻辑
    if [ "$idle_threads" -eq 0 ] && [ "$waiting_requests" -gt 5 ]; then
        log "检测到线程池耗尽: 无空闲线程且存在等待请求"
        return 0
    else
        log "线程池状态正常"
        return 1
    fi
}

# 备份Apache配置文件（不变）
backup_config() {
    local config_file=$1
    if [ ! -d "$BACKUP_DIR" ]; then
        mkdir -p "$BACKUP_DIR"
        log "创建配置备份目录: $BACKUP_DIR"
    fi
    
    local backup_file="$BACKUP_DIR/$(basename "$config_file").$(date +%Y%m%d_%H%M%S)"
    cp "$config_file" "$backup_file"
    log "已备份配置文件至: $backup_file"
}

# 添加或更新ServerName配置（不变）
set_server_name() {
    local config_file=$1
    if ! grep -q "^[[:space:]]*ServerName" "$config_file"; then
        log "未找到ServerName配置，添加默认值: $DEFAULT_SERVER_NAME"
        sed -i "1i ServerName $DEFAULT_SERVER_NAME" "$config_file"
    else
        log "ServerName已配置，无需修改"
    fi
}

# 调整线程池最大连接数（不变）
adjust_thread_pool() {
    local config_file=$1
    local current_value
    local new_value
    local param_name

    if grep -q "MaxRequestWorkers" "$config_file"; then
        param_name="MaxRequestWorkers"
        current_value=$(grep "^[[:space:]]*MaxRequestWorkers" "$config_file" | awk '{print $2}')
    elif grep -q "MaxClients" "$config_file"; then
        param_name="MaxClients"
        current_value=$(grep "^[[:space:]]*MaxClients" "$config_file" | awk '{print $2}')
    else
        log "未找到MaxRequestWorkers或MaxClients配置，将添加默认配置"
        param_name="MaxRequestWorkers"
        current_value=256
        echo -e "\n# 自动添加的线程池配置" >> "$config_file"
        echo "$param_name $current_value" >> "$config_file"
    fi

    new_value=$(( current_value + (current_value * INCREASE_PERCENT / 100) ))
    log "调整$param_name: $current_value -> $new_value (增加$INCREASE_PERCENT%)"

    sed -i "s/^[[:space:]]*$param_name.*/$param_name $new_value/" "$config_file"
    
    if grep -q "ServerLimit" "$config_file"; then
        local server_limit=$(grep "^[[:space:]]*ServerLimit" "$config_file" | awk '{print $2}')
        if [ "$new_value" -gt "$server_limit" ]; then
            log "同步调整ServerLimit: $server_limit -> $new_value"
            sed -i "s/^[[:space:]]*ServerLimit.*/ServerLimit $new_value/" "$config_file"
        fi
    fi
    
    return 0
}

# 验证Apache配置并重启服务（不变）
restart_apache() {
    log "验证Apache配置并重启服务..."
    
    local config_file=$(find_apache_config)
    if [ -n "$config_file" ]; then
        set_server_name "$config_file"
    fi

    if command -v apache2ctl &> /dev/null; then
        if ! apache2ctl configtest; then
            log "Apache配置验证失败，可能需要人工干预"
            return 1
        fi
        systemctl restart apache2
    elif command -v httpd &> /dev/null; then
        if ! httpd -t; then
            log "Apache配置验证失败，可能需要人工干预"
            return 1
        fi
        systemctl restart httpd
    else
        log "无法验证和重启Apache服务"
        return 1
    fi

    if check_apache_running; then
        log "Apache服务重启成功"
        return 0
    else
        log "Apache服务重启失败"
        return 1
    fi
}

# 检查URL重写规则（不变）
check_rewrite_rules() {
    log "检查URL重写规则是否存在潜在问题..."
    
    local rewrite_configs=$(find /etc/apache2 /etc/httpd -name "rewrite.conf" 2>/dev/null)
    
    if [ -z "$rewrite_configs" ]; then
        log "未找到URL重写配置文件"
        return 0
    fi
    
    local problematic_rules=$(grep -r "RewriteRule" $rewrite_configs | grep -E "(R=500|loop|.*\$1.*\$1)")
    
    if [ -n "$problematic_rules" ]; then
        log "警告: 可能存在导致循环的URL重写规则:"
        echo "$problematic_rules" | while read -r line; do
            log "  $line"
        done
        log "建议检查并修正上述重写规则以防止线程阻塞"
        return 1
    else
        log "未发现明显有问题的URL重写规则"
        return 0
    fi
}

# 主函数（不变）
main() {
    log "===== Apache线程池修复脚本启动 ====="
    
    if ! check_apache_running; then
        log "Apache未运行，尝试启动服务..."
        restart_apache
        return 0
    fi
    
    if ! detect_thread_pool_exhaustion; then
        log "线程池未耗尽，无需调整"
        return 0
    fi
    
    check_rewrite_rules
    
    local config_file=$(find_apache_config)
    if [ -z "$config_file" ]; then
        log "无法找到Apache配置文件，退出"
        return 1
    fi
    log "找到Apache配置文件: $config_file"
    
    local adjustment_count=0
    while [ $adjustment_count -lt $MAX_ADJUSTMENTS ]; do
        backup_config "$config_file"
        adjust_thread_pool "$config_file"
        
        if restart_apache; then
            sleep 10
            if ! detect_thread_pool_exhaustion; then
                log "线程池调整成功，问题已解决"
                return 0
            else
                log "线程池仍然耗尽，准备进行第$((adjustment_count+2))次调整"
                adjustment_count=$((adjustment_count + 1))
            fi
        else
            log "Apache重启失败，停止自动调整"
            return 1
        fi
    done
    
    log "已达到最大调整次数($MAX_ADJUSTMENTS)，但问题仍未解决，建议人工干预"
    return 1
}

main
